package com.changgou.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.changgou.search.dao.SkuEsMapper;
import com.changgou.goods.feign.SkuFeign;
import com.changgou.goods.pojo.Sku;
import com.changgou.search.pojo.SkuInfo;
import com.changgou.search.service.SkuService;
import entity.Result;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;

@Service
public class SkuServiceImpl implements SkuService {

    @Autowired
    private SkuEsMapper skuEsMapper;
    @Autowired
    private SkuFeign skuFeign;
    @Autowired
    private ElasticsearchTemplate template;

    @Override
    public void importSku() {
        template.putMapping(SkuInfo.class);

        //调用goods微服务获取数据库sku数据
        Result<List<Sku>> result = skuFeign.findByStatus("1");
        //将sku数据转化为skuInfo数据
        String skuJson = JSON.toJSONString(result.getData());
        List<SkuInfo> skuInfos = JSON.parseArray(skuJson, SkuInfo.class);
        //将spec信息封装成map
        //spec里面的数据就已经是jason字符串的格式，因此只需要直接解析就行
        for (SkuInfo skuInfo : skuInfos) {
            Map<String, Object> specMap = JSON.parseObject(skuInfo.getSpec());
            skuInfo.setSpecMap(specMap);
        }
        //将数据存入es索引库
        skuEsMapper.saveAll(skuInfos);
    }

    @Override
    public Map search(Map<String, String> searchMap) {
        //条件构建
        NativeSearchQueryBuilder nativeSearchQueryBuilder = buildBasicQuery(searchMap);

        //根据条件过滤搜索，商品展示数据
        Map resultMap = searchList(nativeSearchQueryBuilder);

        //分组搜索，分类、品牌、规格等选项数据
        Map groupMap = searchGroupList(nativeSearchQueryBuilder, searchMap);

        resultMap.putAll(groupMap);
        return resultMap;
    }

    /****
     * 构建查询条件
     * @param searchMap
     * @return
     */
    private NativeSearchQueryBuilder buildBasicQuery(Map<String, String> searchMap) {
        //准备搜索条件
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();

        //多条件组合查询
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();

        if (searchMap != null) {
            //关键词查询
            if (!StringUtils.isEmpty(searchMap.get("keywords"))) {
                //nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("name", searchMap.get("keywords")));
                queryBuilder.must(QueryBuilders.matchQuery("name", searchMap.get("keywords")));
            }
            //分类过滤
            if (!StringUtils.isEmpty(searchMap.get("category"))) {
                queryBuilder.must(QueryBuilders.matchQuery("categoryName", searchMap.get("category")));
            }
            //品牌过滤
            if (!StringUtils.isEmpty(searchMap.get("brand"))) {
                queryBuilder.must(QueryBuilders.matchQuery("brandName", searchMap.get("brand")));
            }
            //规格过滤，多个规格循环遍历
            for (String key : searchMap.keySet()) {
                if (key.startsWith("spec_")) {
                    queryBuilder.must(QueryBuilders.matchQuery("specMap." + key.substring(5) + ".keyword", searchMap.get(key)));
                }
            }
            //价格区间检索
            if (!StringUtils.isEmpty(searchMap.get("price"))) {
                //去掉元和以上
                String price = searchMap.get("price").replace("元", "").replace("以上", "");
                //根据-分割获得价格区间
                String[] priceRange = price.split("-");
                //priceRange[0]表示下限
                if (!priceRange[0].equalsIgnoreCase("")) {
                    queryBuilder.must(QueryBuilders.rangeQuery("price").gt(Integer.parseInt(priceRange[0])));
                }
                //priceRange[1]表示上限
                if (priceRange.length == 2) {
                    queryBuilder.must(QueryBuilders.rangeQuery("price").lte(Integer.parseInt(priceRange[1])));
                }
            }
            //排序
            String sortRule = searchMap.get("sortRule");
            String sortFeild = searchMap.get("sortFeild");
            if (!StringUtils.isEmpty(sortFeild)) {
                nativeSearchQueryBuilder.withSort(
                        SortBuilders.fieldSort(sortFeild) //排序的域
                                .order(SortOrder.valueOf(sortRule))); //排序的规则
            }
        }

        //设置分页
        Integer currentPage = getCurrentPage(searchMap);
        Integer pageSize = 30;
        PageRequest pageRequest = PageRequest.of(currentPage - 1, pageSize);
        nativeSearchQueryBuilder.withPageable(pageRequest);

        //添加过滤规则
        nativeSearchQueryBuilder.withQuery(queryBuilder);
        return nativeSearchQueryBuilder;
    }

    /****
     * 获取当前页数据，如果没有则默认为第一页
     * @param searchMap
     * @return
     */
    private Integer getCurrentPage(Map<String, String> searchMap) {
        if (searchMap != null && !StringUtils.isEmpty(searchMap.get("pageNum"))) {
            try {
                return Integer.parseInt(searchMap.get("pageNum"));
            } catch (NumberFormatException e) {

            }
        }
        return 1;
    }

    /****
     * 执行主体内容分页搜索，实现高亮搜索
     * @param builder
     * @return
     */
    private Map searchList(NativeSearchQueryBuilder builder) {
        //指定高亮域
        HighlightBuilder.Field field = new HighlightBuilder.Field("name");  //指定高亮域的名字
        field.preTags("<em style='color:red;'>");
        field.postTags("</em>");
        //显示高亮数据的长度
        field.fragmentSize(100);
        //开启高亮
        builder.withHighlightFields(field);

        //执行高亮搜索
        AggregatedPage<SkuInfo> skuInfos = template.queryForPage(builder.build(), SkuInfo.class, new SearchResultMapper() {
            /***
             * 数据+分页
             * @param searchResponse:查询之后的数据
             * @param aClass
             * @param pageable:分页数据的封装
             * @param <T>
             * @return
             */
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                //创建一个集合存储高亮数据
                List<T> list = new ArrayList<>();

                SearchHits hits = searchResponse.getHits();
                for (SearchHit hit : hits) {
                    //获取非高亮数据  {"name":"华为手机","iamge":"http://www.itheima.com/1.jpg"}
                    String json = hit.getSourceAsString();
                    SkuInfo skuInfo = JSON.parseObject(json, SkuInfo.class);

                    //获取高亮数据
                    HighlightField name = hit.getHighlightFields().get("name");
                    if (name != null) {
                        //存储高亮数据
                        StringBuffer stringBuffer = new StringBuffer();
                        for (Text text : name.getFragments()) {
                            stringBuffer.append(text.toString());
                        }
                        //将非高亮数据替换成高亮数据
                        skuInfo.setName(stringBuffer.toString());
                    }
                    list.add((T) skuInfo);
                }
                /***
                 * 1:高亮集合数据
                 * 2:分页对象
                 * 3:总记录数
                 */
                return new AggregatedPageImpl<T>(list, pageable, searchResponse.getHits().getTotalHits());
            }
        });

        //创建map，封装返回的数据
        Map resultMap = new HashMap();
        //总记录条数
        long totalElements = skuInfos.getTotalElements();
        resultMap.put("total", totalElements);
        //总页数
        resultMap.put("totalPages", skuInfos.getTotalPages());
        //获取拿到的结果
        resultMap.put("rows", skuInfos.getContent());

        //获取分页数据
        resultMap.put("pageNum", builder.build().getPageable().getPageNumber() + 1); //当前页
        resultMap.put("pageSize", builder.build().getPageable().getPageSize());    //每页显示条数
        return resultMap;
    }

    /****
     * 执行分组选项菜单搜索（产品分类、品牌、规格等）
     * @param builder
     * @param searchMap
     * @return
     */
    private Map searchGroupList(NativeSearchQueryBuilder builder, Map<String, String> searchMap) {
        if (searchMap == null || StringUtils.isEmpty(searchMap.get("category"))) {
            //产品分类
            builder.addAggregation(AggregationBuilders.terms("skuCategory").field("categoryName"));
        }
        if (searchMap == null || StringUtils.isEmpty(searchMap.get("brand"))) {
            //品牌分类
            builder.addAggregation(AggregationBuilders.terms("skuBrand").field("brandName"));
        }
        //由于规格有很多种，因此即使搜索条件中有一种规格，也还有其他种类的规格需要分组搜索
        builder.addAggregation(AggregationBuilders.terms("skuSpec").field("spec.keyword"));

        //将所有的分组条件设置好之后再一次查询，避免多次连接es数据库，提高效率
        AggregatedPage<SkuInfo> groupResult = template.queryForPage(builder.build(), SkuInfo.class);
        //拿到所有聚合查询的结果
        Aggregations aggregations = groupResult.getAggregations();

        Map resultMap = new HashMap();
        //拿到产品分类数据
        if (searchMap == null || StringUtils.isEmpty(searchMap.get("category"))) {
            resultMap.put("categoryList", getGroupData(aggregations, "skuCategory"));
        }
        //拿到品牌数据
        if (searchMap == null || StringUtils.isEmpty(searchMap.get("brand"))) {
            resultMap.put("brandList", getGroupData(aggregations, "skuBrand"));
        }
        //拿到规格分类数据
        List<String> specList = getGroupData(aggregations, "skuSpec");
        Map<String, Set<String>> specData = getSpecData(specList);
        resultMap.put("specList", specData);

        return resultMap;
    }

    /****
     * 从分组结果中提取分组数据
     * @param aggregations 分组结果
     * @param name 分组别名
     * @return
     */
    private List<String> getGroupData(Aggregations aggregations, String name) {
        //根据别名获取分组数据
        StringTerms stringTerms = aggregations.get(name);
        //遍历获取所有分组数据
        List<String> list = new ArrayList<>();
        for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {
            String groupResult = bucket.getKeyAsString();
            list.add(groupResult);
        }
        return list;
    }

    private Map<String, Set<String>> getSpecData(List<String> specList) {
        //规格的参数，使用set能去重
        Map<String, Set<String>> resultMap = new HashMap<>();

        //遍历获取规格数据
        for (String spec : specList) {
            //jason字符串格式需要转化为map，{"电视音响效果":"小影院","电视屏幕尺寸":"20英寸","尺码":"165"}
            Map<String, String> specMap = JSON.parseObject(spec, Map.class);

            for (String key : specMap.keySet()) {
                Set<String> params = resultMap.get(key);
                if (params == null) {
                    //还没有这项规格参数,则需要新创建
                    params = new HashSet<>();
                    params.add(specMap.get(key));
                    resultMap.put(key, params);
                } else {
                    //如果有则只需要将参数添加进去即可
                    params.add(specMap.get(key));
                }
            }
        }
        return resultMap;
    }
}
